一、布局文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".MainActivity">
<GridLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:columnCount="1"
android:padding="20dp"
android:rowCount="5"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent">
<Button
android:id="@+id/btCamera"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="自定义 Camera 拍照" />
<Button
android:id="@+id/btCameraRecord"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="自定义 Camera 录视频" />
</GridLayout>
</RelativeLayout>
二、拍照、录视频的activity代码
package com.chy.CCamera1;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.RectF;
import android.hardware.Camera;
import android.os.Bundle;
import android.view.SurfaceView;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.chy.myActivity.R;
import com.chy.util.BitmapUtil;
import com.chy.util.FileUtil;
import com.chy.view.FaceView;
import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
/**
* 自定义拍照 1
* */
public class CameraActivity1 extends AppCompatActivity {
public static final String TYPE_TAG = "TYPE";
public static final int TYPE_CAPTURE = 0;// 拍照标记
public static final int TYPE_RECORD = 1;// 录制视频标记
//控制MediaRecorderHelper的初始化
private boolean lock = false;
private CameraHelper1 mCameraHelper;
private MediaRecorderHelper mMediaRecorderHelper = null;
private SurfaceView surfaceView;
private FaceView faceView;
private ImageView ivSetting;// 设置
private ImageButton btnTakePic;// 拍照
private ImageView ivStart;// 开始录制视频
private ImageView ivStop;
private ImageView ivExchange;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera1);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
initView();// 初始化控件
// 跳转录像界面
videoMethod();
mCameraHelper = new CameraHelper1(this, surfaceView);
// 调用回调函数
mCameraHelper.addCallBack(new CameraHelper1.CallBack() {
@Override
public void onPreviewFrame(byte[] data) {
if (!lock){
if (mCameraHelper.getmCamera() != null){
mMediaRecorderHelper = new MediaRecorderHelper(CameraActivity1.this,
mCameraHelper.getmCamera(),mCameraHelper.mDisplayOrientation,
mCameraHelper.mSurfaceHolder.getSurface());
}
lock = true;
}
}
@Override
public void onTakePic(final byte[] data) {
new Thread(new Runnable() {
@Override
public void run() {
savePic(data);
}
}).start();
btnTakePic.setClickable(true);
}
@Override
public void onFaceDetect(ArrayList<RectF> faces) {
faceView.setmFaces(faces);
}
});
}
/**
* 控件初始化
* */
private void initView(){
// surfaceView
surfaceView = findViewById(R.id.surfaceView);
// faceView
faceView = findViewById(R.id.faceView);
// setting
ivSetting = findViewById(R.id.ivSetting);
ivSetting.setOnClickListener(setMethod);
// take pic
btnTakePic = findViewById(R.id.btnTakePic);
btnTakePic.setOnClickListener(takePic);
// start
ivStart = findViewById(R.id.ivStart);
ivStart.setOnClickListener(startVideo);
// stop
ivStop = findViewById(R.id.ivStop);
ivStop.setOnClickListener(stopVideo);
// exchange
ivExchange = findViewById(R.id.ivExchange);
ivExchange.setOnClickListener(exchangeMethod);
}
/**
* 录制视频方法
* */
private void videoMethod(){
if (getIntent().getIntExtra(TYPE_TAG,0) == TYPE_RECORD){
btnTakePic.setVisibility(View.GONE);
ivStart.setVisibility(View.VISIBLE);
}
}
/**
* 保存图片方法
* */
private void savePic(final byte[] data){
try {
// 系统当前时间
final long temp = System.currentTimeMillis();
FileUtil fileUtil = new FileUtil(getApplicationContext());
final File picFile = fileUtil.createCameraFile("camera1");
if (picFile != null && data != null){
Bitmap rawBitMap = BitmapFactory.decodeByteArray(data, 0, data.length);
Bitmap resultBitMap = null;
if (mCameraHelper.mCameraFacing == Camera.CameraInfo.CAMERA_FACING_FRONT){
resultBitMap = BitmapUtil.mirror(BitmapUtil.rotate(rawBitMap, 270f));
}else {
resultBitMap = BitmapUtil.rotate(rawBitMap, 90f);
}
byte[] result = BitmapUtil.toByteArray(resultBitMap);
// 输出文件
FileOutputStream fos = new FileOutputStream(picFile);
fos.write(result, 0, result.length);
fos.flush();
fos.close();
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(),
"图片已保存!",Toast.LENGTH_SHORT).show();
long time_consuming = System.currentTimeMillis() - temp;
System.out.println("耗时:"+time_consuming);
System.out.println("路径:"+picFile.getAbsolutePath());
}
});
}
}catch (Exception e){
e.printStackTrace();
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(),
"保存图片失败!",Toast.LENGTH_SHORT).show();
}
});
}
}
@Override
protected void onDestroy() {
mCameraHelper.releaseCamera();
if (mMediaRecorderHelper != null && mMediaRecorderHelper.isRunning){
mMediaRecorderHelper.stopRecord();
mMediaRecorderHelper.release();
}
super.onDestroy();
}
/**
* 设置点击事件
* */
private View.OnClickListener setMethod = new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(getApplicationContext(),"setting", Toast.LENGTH_SHORT).show();
}
};
/**
* 拍照点击事件
* */
private View.OnClickListener takePic = new View.OnClickListener() {
@Override
public void onClick(View v) {
mCameraHelper.takePic();
}
};
/**
* 切换摄像头
* */
private View.OnClickListener exchangeMethod = new View.OnClickListener() {
@Override
public void onClick(View v) {
mCameraHelper.exchangeCamera();
lock = false;
}
};
/**
* 开始录制视频
* */
private View.OnClickListener startVideo = new View.OnClickListener() {
@Override
public void onClick(View v) {
ivExchange.setClickable(false);
ivStart.setVisibility(View.GONE);
ivStop.setVisibility(View.VISIBLE);
mMediaRecorderHelper.startRecord();
}
};
/**
* 停止录制视频
* */
private View.OnClickListener stopVideo = new View.OnClickListener() {
@Override
public void onClick(View v) {
ivStart.setVisibility(View.VISIBLE);
ivStop.setVisibility(View.GONE);
ivExchange.setClickable(true);
mMediaRecorderHelper.stopRecord();
}
};
}
三、调用摄像头工具类代码
package com.chy.CCamera1;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.hardware.Camera;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.Toast;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* 摄像头工具类
* */
public class CameraHelper1 implements Camera.PreviewCallback {
private Camera mCamera = null;//Camera对象
private Camera.Parameters mParameters;//Camera对象的参数
private SurfaceView mSurfaceView;//用于预览的SurfaceView对象
public SurfaceHolder mSurfaceHolder;//SurfaceHolder对象
private Activity mActivity;
private CallBack mCallBack = null;//自定义的回调
//默认设置为后置摄像
public int mCameraFacing = Camera.CameraInfo.CAMERA_FACING_BACK;
public int mDisplayOrientation = 0;//预览旋转的角度
private int picWidth = 2160;//保存图片的宽
private int picHeight = 3840;//保存图片的高
public CameraHelper1(Activity _activity, SurfaceView _surfaceView){
this.mActivity = _activity;
this.mSurfaceView = _surfaceView;
mSurfaceHolder = mSurfaceView.getHolder();
init();
}
@Override
public void onPreviewFrame(byte[] data,Camera camera) {
mCallBack.onPreviewFrame(data);
}
/**
* 拍照方法
* */
public void takePic(){
if (mCamera != null){
mCamera.takePicture(null,null, new Camera.PictureCallback() {
@Override
public void onPictureTaken(byte[] data,Camera camera) {
mCamera.startPreview();
mCallBack.onTakePic(data);
}
});
}
}
/**
* 初始化方法
* */
public void init(){
mSurfaceHolder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
if (mCamera == null){
openCamera(mCameraFacing);
}
startPreview();
}
@Override
public void surfaceChanged(SurfaceHolder holder,int format,int width,int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
releaseCamera();
}
});
}
/**
* 打开相机
* @param direction 摄像头方向
* */
private boolean openCamera(int direction){
boolean supportCameraFacing = supportCameraFacing(direction);
if (supportCameraFacing){
try {
mCamera = Camera.open(direction);
initParameters(mCamera);
mCamera.setPreviewCallback(this);
}catch (Exception e){
e.printStackTrace();
Toast.makeText(mActivity, "打开相机失败!", Toast.LENGTH_SHORT).show();
return false;
}
}
return supportCameraFacing;
}
/**
* 相机配置参数
* */
private void initParameters(Camera camera){
try {
mParameters = camera.getParameters();
// 开启闪光灯
//mParameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
// 关闭闪光灯
//mParameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
// 设图像格式
mParameters.setPreviewFormat(ImageFormat.NV21);
//获取与指定宽高相等或最接近的尺寸
//设置预览尺寸
Camera.Size bestPreviewSize = getBestSize(mSurfaceView.getWidth(), mSurfaceView.getHeight(), mParameters.getSupportedPreviewSizes());
if (bestPreviewSize != null){
mParameters.setPreviewSize(bestPreviewSize.width, bestPreviewSize.height);
}
// 设置保存图片尺寸
Camera.Size bestPicSize = getBestSize(picWidth, picHeight, mParameters.getSupportedPictureSizes());
if (bestPicSize != null){
mParameters.setPictureSize(bestPicSize.width, bestPicSize.height);
}
// 对焦模式
if (isSupportFocus(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE))
mParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
camera.setParameters(mParameters);
}catch (Exception e){
e.printStackTrace();
Toast.makeText(mActivity, "相机初始化失败!", Toast.LENGTH_SHORT).show();
}
}
/**
* 开始预览
* */
private void startPreview(){
if (mCamera != null){
try {
mCamera.setPreviewDisplay(mSurfaceHolder);
setCameraDisplayOrientation(mActivity);
mCamera.startPreview();
startFaceDetect();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void startFaceDetect() {
if (mCamera != null){
mCamera.startFaceDetection();
mCamera.setFaceDetectionListener(new Camera.FaceDetectionListener() {
@Override
public void onFaceDetection(Camera.Face[] faces,Camera camera) {
mCallBack.onFaceDetect(transForm(faces));
Log.v("tag", "检测到"+faces.length+"张人脸");
}
});
}
}
/**
* 判断是否支持某一对焦模式
*/
private Boolean isSupportFocus(String focusMode) {
boolean autoFocus = false;
List<String> listFocusMode = mParameters.getSupportedFocusModes();
for (String mode :listFocusMode) {
if (mode == focusMode)
autoFocus = true;
Log.v("tag","相机支持的对焦模式:"+mode);
}
return autoFocus;
}
/**
* 切换摄像头
* */
public void exchangeCamera() {
releaseCamera();
if (mCameraFacing == Camera.CameraInfo.CAMERA_FACING_BACK){
mCameraFacing =Camera.CameraInfo.CAMERA_FACING_FRONT;// 前置摄像机
} else {
mCameraFacing = Camera.CameraInfo.CAMERA_FACING_BACK;// 后置摄像机
}
if (mCamera == null){
openCamera(mCameraFacing);
}
startPreview();
}
/**
* 释放摄像头资源
*/
public void releaseCamera() {
if (mCamera == null) {
return;
}
try {
mCamera.stopPreview();
mCamera.setPreviewDisplay(null);
mCamera.setPreviewCallback(null);
mCamera.lock();
mCamera.release();
mCamera = null;
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 获取与指定宽高相等或最接近的尺寸
* */
private Camera.Size getBestSize(int targetWidth,int targetHeight,List<Camera.Size> sizeList){
Camera.Size bestSize = null;
double targetRatio = Double.valueOf(targetHeight) / targetWidth;
double minDiff = targetRatio;
for (Camera.Size size:sizeList) {
double supportedRatio = Double.valueOf(size.width)/size.height;
Log.v("系统支持的尺寸:"+size.width+"*"+size.height, "比例:"+supportedRatio);
}
for (Camera.Size size:sizeList) {
if (size.width == targetHeight && size.height == targetWidth){
bestSize = size;
break;
}
double supportedRatio = Double.valueOf(size.width)/size.height;
if (Math.abs(supportedRatio - targetRatio) < minDiff){
minDiff = Math.abs(supportedRatio - targetRatio);
bestSize = size;
}
}
Log.v("目标尺寸:"+targetWidth+"*"+targetHeight, "比例:"+targetRatio);
Log.v("最优尺寸",bestSize.height+"*"+bestSize.width);
return bestSize;
}
/**
* 解决预览变形问题
*
* @param sizes
* @param w
* @param h
* @return
*/
/*private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {
final double aspectTolerance = 0.1;
double targetRatio = (double) w / h;
if (sizes == null) {
return null;
}
Camera.Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
// Try to find an size match aspect ratio and size
for (Camera.Size size : sizes) {
double ratio = (double) size.width / size.height;
if (Math.abs(ratio - targetRatio) > aspectTolerance) {
continue;
}
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
// Cannot find the one match the aspect ratio, ignore the requirement
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Camera.Size size : sizes) {
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}
return optimalSize;
}*/
/**
* 设置预览旋转角度
* */
private void setCameraDisplayOrientation(Activity activity){
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(mCameraFacing, info);
int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
int screenDegree = 0;
switch (rotation){
case Surface.ROTATION_0:// 角度为0
screenDegree = 0;
break;
case Surface.ROTATION_90:// 角度为90
screenDegree = 90;
break;
case Surface.ROTATION_180:// 角度为180
screenDegree = 180;
break;
case Surface.ROTATION_270:// 角度为270
screenDegree = 270;
break;
}
// 相机旋转角度
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT){
mDisplayOrientation = (info.orientation + screenDegree) % 360;
mDisplayOrientation = (360 - mDisplayOrientation) % 360;// 补偿后视镜
}else {
mDisplayOrientation = (info.orientation - screenDegree + 360) % 360;
}
mCamera.setDisplayOrientation(mDisplayOrientation);
Log.v("tag","屏幕的旋转角度 : "+rotation);
Log.v("tag","setDisplayOrientation(result) : "+mDisplayOrientation);
}
/**
* 判断是否支持某个相机
* */
private Boolean supportCameraFacing(int cameraFacing) {
Camera.CameraInfo info = new Camera.CameraInfo();
for (int cameraIndex=0; cameraIndex<Camera.getNumberOfCameras(); cameraIndex++) {
Camera.getCameraInfo(cameraIndex, info);
if (info.facing == cameraFacing)
return true;
}
return false;
}
/**
* 将相机中用于表示人脸矩形的坐标转换成UI页面的坐标
*/
private ArrayList<RectF> transForm(Camera.Face[] faces) {
Matrix matrix = new Matrix();
// 设置为前摄像头 - Need mirror for front camera.
mCameraFacing = Camera.CameraInfo.CAMERA_FACING_FRONT;
int mirror = mCameraFacing;
if (mirror>1){
matrix.setScale(-1f,1f);
}else {
matrix.setScale(1f,1f);
}
// This is the value for android.hardware.Camera.setDisplayOrientation.
matrix.postRotate(Float.valueOf(mDisplayOrientation));
// Camera driver coordinates range from (-1000, -1000) to (1000, 1000).
// UI coordinates range from (0, 0) to (width, height).
matrix.postScale(mSurfaceView.getWidth() / 2000f, mSurfaceView.getHeight() / 2000f);
matrix.postTranslate(mSurfaceView.getWidth() / 2f, mSurfaceView.getHeight() / 2f);
ArrayList<RectF> rectList = new ArrayList<RectF>();
for (Camera.Face face : faces) {
RectF srcRect = new RectF(face.rect);
RectF dstRect = new RectF(0f,0f,0f,0f);
matrix.mapRect(dstRect, srcRect);
rectList.add(dstRect);
}
return rectList;
}
public Camera getmCamera(){
Camera camera = null;
if (mCamera != null)
camera = mCamera;
return camera;
}
public void addCallBack(CallBack callBack){
this.mCallBack = callBack;
}
/**
* 回调函数接口
* */
public interface CallBack{
void onPreviewFrame(byte[] data);
void onTakePic(byte[] data);
void onFaceDetect(ArrayList<RectF> faces);
}
/**
* 检测摄像头是否存在
* */
private boolean checkCameraHardware(Context context) {
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
return true;
} else {
return false;
}
}
}
四、录像工具类
package com.chy.CCamera1;
import android.app.Activity;
import android.hardware.Camera;
import android.media.MediaRecorder;
import android.util.Log;
import android.view.Surface;
import android.widget.Toast;
import com.chy.util.FileUtil;
/**
* 录像工具类
* */
public class MediaRecorderHelper {
private MediaRecorder mMediaRecorder = null;
public boolean isRunning = false;
private String filePath = null;
private Activity mActivity;
private Camera mCamera;
private int rotation;
private Surface surface;
/**
* 构造函数
* */
public MediaRecorderHelper(Activity _mActivity, Camera _mCamera, int _rotation, Surface _surface){
this.mActivity = _mActivity;
this.mCamera = _mCamera;
this.rotation = _rotation;
this.surface = _surface;
initFilePath();
}
/**
* 初始化文件方法
* */
private void initFilePath(){
FileUtil fileUtil = new FileUtil(mActivity.getApplicationContext());
filePath = fileUtil.createVideoFile().getAbsolutePath();
}
public void startRecord(){
mMediaRecorder = new MediaRecorder();
if (mMediaRecorder != null){
try {
mCamera.unlock();// 必须调用
mMediaRecorder.reset();
mMediaRecorder.setCamera(mCamera);
mMediaRecorder.setOrientationHint(rotation);//改变保存后的视频文件播放时是否横屏(不加这句,视频文件播放的时候角度是反的)
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);//设置从麦克风采集声音
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);//设置从摄像头采集图像
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);//设置视频的输出格式为MP4
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);//设置音频的编码格式
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);// 设置视频的编码格式
mMediaRecorder.setVideoSize(176, 144);// 设置视频大小.必须放在设置编码和格式的后面,否则报错
mMediaRecorder.setVideoFrameRate(20);// 设置帧率
//it.setMaxDuration(10000) //设置最大录像时间为10s
mMediaRecorder.setPreviewDisplay(surface);//设置
mMediaRecorder.setOutputFile(filePath);//设置输出文件
mMediaRecorder.prepare();
mMediaRecorder.start();
isRunning = true;
Log.d("tag", "开始录制视频");
Log.d("tag", "视频保存路径:"+filePath);
}catch (Exception e){
e.printStackTrace();
}
}
}
public void stopRecord(){
if (mMediaRecorder != null){
mMediaRecorder.stop();
isRunning = false;
Log.d("tag", "停止录制视频");
Toast.makeText(mActivity, "视频保存路径"+filePath, Toast.LENGTH_SHORT).show();
}
}
public void release(){
if (mMediaRecorder != null){
mMediaRecorder.release();
mMediaRecorder = null;
isRunning = false;
}
}
}
五、使用方法
/**
* 按钮点击事件
* */
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btCamera:// Camera1 拍照事件
PermissionUtil.getInstance().checkPermission(this,permissions,new Runnable() {
@Override
public void run() {
Intent intent = new Intent(MainActivity.this,CameraActivity1.class);
intent.putExtra(CameraActivity1.TYPE_TAG, CameraActivity1.TYPE_CAPTURE);
startActivity(intent);
}
});
break;
case R.id.btCameraRecord:// Camera 录视频按钮
PermissionUtil.getInstance().checkPermission(MainActivity.this,permissions,new Runnable() {
@Override
public void run() {
Intent intent = new Intent(MainActivity.this,CameraActivity1.class);
intent.putExtra(CameraActivity1.TYPE_TAG, CameraActivity1.TYPE_RECORD);
startActivity(intent);
}
});
break;
}
}
六、权限动态获取方法
1、在AndroidManifest.xml中填写如下权限
<uses-permission android:name="android.permission.INTERNET" /> <!-- 网络权限 --> <uses-permission android:name="android.permission.CAMERA" /> <!-- 相机权限 --> <uses-permission android:name="android.permission.RECORD_AUDIO" /><!-- 音频录制权限--> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /><!-- 定位权限 --> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <!-- 获取基站的服务信号权限,以便获取位置信息 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- 写入数据权限 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!--读取数据权限 -->
2、在MainActivity.java中定义权限
// 动态申请权限 private String[] permissions = { Manifest.permission.INTERNET,// 网络权限 Manifest.permission.CAMERA,// 相机权限 Manifest.permission.RECORD_AUDIO,// 音频录制权限 Manifest.permission.ACCESS_FINE_LOCATION,// 定位权限 Manifest.permission.WRITE_EXTERNAL_STORAGE,// 写入数据权限 Manifest.permission.READ_EXTERNAL_STORAGE,// 读取数据权限 Manifest.permission.ACCESS_COARSE_LOCATION // 获取基站的服务信号权限,以便获取位置信息 };
3、创建动态申请权限工具类
package com.chy.util;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.provider.Settings;
import android.util.Log;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
public class PermissionUtil {
private final int PERMISSION_REQUEST_CODE = 100;
private final int PERMISSION_SETTING_CODE = 101;
private AlertDialog permissionExplainDialog = null;
private AlertDialog permissionSettingDialog = null;
private PermissionUtil(){}
public static PermissionUtil getInstance(){
return PermissionUtilHolder.instance;
}
private static class PermissionUtilHolder{
private static final PermissionUtil instance = new PermissionUtil();
}
/**
* 第一步,检查权限
*
* @param activity 上下文
* @param permissions 权限数组
* @param callBack 线程
* */
public void checkPermission(AppCompatActivity activity, String[] permissions, Runnable callBack){
boolean allGranted = true;
for (String permission : permissions) {
int result = ContextCompat.checkSelfPermission(activity, permission);
Log.d("检查权限:"+permission,"结果:"+result);
// 有权限: PackageManager.PERMISSION_GRANTED
// 无权限: PackageManager.PERMISSION_DENIED
if (result != PackageManager.PERMISSION_GRANTED){// 有权限
allGranted = false;
Toast.makeText(activity,"拥有"+permission+"权限",Toast.LENGTH_SHORT).show();
}
}
if (allGranted){// 拥有全部权限
callBack.run();
}else {// 申请权限
startRequestPermission(activity, permissions);
}
}
/**
* 第二步,如果用户之前拒绝过,展示需要权限的提示框,否则的话直接请求相关权限
*
* @param activity 上下文
* @param permissions 权限数组
* */
private void startRequestPermission(AppCompatActivity activity, String[] permissions){
for (String permission : permissions) {
/**
* shouldShowRequestPermissionRationale
* 如果应用之前请求过该权限但用户拒绝了该方法就会返回true
*
* 如果用户之前拒绝了权限请求并且勾选了权限请求对话框的”不再询问”,该方法会返回false,
* 如果设备策略禁止该应用获得该权限也会返回false
*/
if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)){
// 向用户显示一个解释,要以异步非阻塞的方式
// 该线程将等待用户响应!等用户看完解释后再继续尝试请求权限
Log.v("tag", "showPermissionExplainDialog()");
showPermissionExplainDialog(activity, permissions);
}else {
/**
* 当你的应用调用requestPermissions()方法时,系统会向用户展示一个标准对话框,
* 你的应用不能修改也不能自定义这个对话框,如果你需要给用户一些额外的信息和解释你就需要在
* 调用requestPermissions()之前像上面一样" 解释为什么应用需要这些权限"
*/
Log.v("tag", "requestPermission");
requestPermission(activity, permissions);
}
}
}
/**
* 不需要向用户解释了,我们可以直接请求该权限
* 第三步. 请求权限
*
* @param activity 上下文
* @param permissions 权限数组
*/
private void requestPermission(AppCompatActivity activity, String[] permissions){
ActivityCompat.requestPermissions(activity, permissions, PERMISSION_REQUEST_CODE);
}
/**
* 当用户之前拒绝过,展示一个对话框,解释为什么需要此权限
*
* @param activity 上下文
* @param permissions 权限数组
*/
private void showPermissionExplainDialog(final AppCompatActivity activity,final String[] permissions){
if (permissionExplainDialog == null){
permissionExplainDialog = new AlertDialog.Builder(activity).setTitle("权限申请").setMessage("您刚才拒绝了相关权限,但是现在应用需要这个权限," +
"点击确定申请权限,点击取消将无法使用该功能")
.setPositiveButton("确定",new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,int which) {
requestPermission(activity, permissions);
dialog.cancel();
}
})
.setNegativeButton("取消",new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,int which) {
dialog.cancel();
}
}).create();
permissionExplainDialog.show();
}
}
/**
* 最后一步,当用户拒绝并且勾选了不在提示,那么只能引导用户去设置页面打开权限
*
* @param activity 上下文
*/
public void showPermissionSettingDialog(final AppCompatActivity activity){
if (permissionSettingDialog == null){
permissionSettingDialog = new AlertDialog.Builder(activity)
.setTitle("权限设置")
.setMessage("您刚才拒绝了相关的权限,请到应用设置页面更改应用的权限")
.setPositiveButton("确定",new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,int which) {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", activity.getPackageName(), null);
intent.setData(uri);
activity.startActivityForResult(intent, PERMISSION_SETTING_CODE);
dialog.cancel();
}
})
.setNegativeButton("取消",new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,int which) {
dialog.cancel();
}
}).create();
permissionSettingDialog.show();
}
}
}
4、使用动态申请权限方法
PermissionUtil.getInstance().checkPermission(this,permissions,new Runnable() {
@Override
public void run() {
// 执行方法
}
});